﻿using System;
using System.Data;
using System.Linq;
using DomainExample.DomainModel.ContactModel;

namespace DomainExample.Repository
{
    public interface IContactRepository : IRepository<Contact, ContactId>
    {

    }

    public sealed class ContactRepository : BaseRepository, IContactRepository
    {
        internal ContactRepository(IDataProvider dataProvider) 
            : base(dataProvider) { }

        public Contact GetById(ContactId contactId)
        {
            const string query = "SELECT Id, DateUtc, Type, Direction, Summary FROM Contact WHERE Id = @id";

            var command = NewCommand(query);
            AddParameter(command, "Id", contactId.Value);

            var results = GetEntitiesFromDatabase<Contact>(command);
            return results.SingleOrDefault();
        }

        public Contact Persist(Contact contact)
        {
            return contact.Id.Value == 0 ? Add(contact) : Edit(contact);
        }

        public void Remove(ContactId contactId)
        {
            const string query = "DELETE FROM Contact WHERE Id = @id";

            var command = NewCommand(query);
            AddParameter(command, "Id", contactId.Value);
            ExecuteNonQuery(command);
        }

        protected override object MapRowToEntity(IDataReader reader)
        {
            var contact = Contact.NewContact(ContactId.NewContactId(reader.GetInt64(reader.GetOrdinal("Id"))));
            contact.DateUtc = DateTime.SpecifyKind(reader.GetDateTime(reader.GetOrdinal("DateUtc")), DateTimeKind.Utc);
            contact.Type = ContactType.Types[reader.GetString(reader.GetOrdinal("Type"))];
            contact.Direction = DirectionType.Types[reader.GetString(reader.GetOrdinal("Direction"))];
            contact.Summary = reader.GetString(reader.GetOrdinal("Summary"));
            return contact;
        }

        private Contact Add(Contact contact)
        {
            const string query = "INSERT INTO Contact (DateUtc, Type, Direction, Summary) VALUES (@dateUtc, @type, @direction, @summary)";
            Int64 newContactId;

            var command = NewCommand(query);
            AddParameter(command, "@dateUtc", contact.DateUtc.ToUniversalTime());
            AddParameter(command, "@type", contact.Type.ToString());
            AddParameter(command, "@direction", contact.Direction.ToString());
            AddParameter(command, "@summary", contact.Summary);

            ExecuteInsert(command, out newContactId);
            contact.Id = ContactId.NewContactId(newContactId);
            return contact;
        }

        private Contact Edit(Contact contact)
        {
            const string query = "UPDATE Contact SET DateUtc = @dateUtc, Type = @type, Direction = @direction, Summary = @summary WHERE Id = @id";

            var command = NewCommand(query);
            AddParameter(command, "@dateUtc", contact.DateUtc.ToUniversalTime());
            AddParameter(command, "@type", contact.Type.ToString());
            AddParameter(command, "@direction", contact.Direction.ToString());
            AddParameter(command, "@summary", contact.Summary);
            AddParameter(command, "id", contact.Id.Value);
            
            ExecuteNonQuery(command);

            return contact;
        }
    }
}
